﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using Citrix.Cmp;
using System.Windows.Input;
using Citrix.XaAdminMobile.Util;
using Citrix.XaAdminMobile.Model;
using System.Threading;

namespace Citrix.XaAdminMobile.ViewModel
{
    /// <summary>
    /// The view-model for the AddServerView. Allows the user to enter in a new XenApp server
    /// to connect to.
    /// </summary>
    internal class AddServerViewModel : INotifyPropertyChanged, IPage
    {
        /// <summary>
        /// A reference back to the root v-m object.
        /// </summary>
        private readonly ViewModelManager vmRoot;

        /// <summary>
        /// The server configuration model object.
        /// </summary>
        private readonly IServerConfigModel model;

        /// <summary>
        /// Holds an error message to show to the user.
        /// </summary>
        private string errorMessage = null;

        /// <summary>
        /// The server name entered by the user.
        /// </summary>
        private string serverName = null;

        /// <summary>
        /// The current status message displayed on the page.
        /// </summary>
        private string statusMessage = null;

        /// <summary>
        /// Tracks whether we are currently executing some long running work.
        /// </summary>
        private bool working = false;

        /// <summary>
        /// Used to sync access to internal state.
        /// </summary>
        private object stateLock = new object();

        /// <summary>
        /// Ctor.
        /// </summary>
        /// <param name="vmMgr">The root v-m object.</param>
        /// <param name="modelSource">The server config model object.</param>
        /// <exception cref="ArgumentNullException">Thrown if any arg is null.</exception>
        public AddServerViewModel(ViewModelManager vmMgr, IServerConfigModel modelSource)
        {
            if (null == vmMgr)
            {
                throw new ArgumentNullException("vmMgr");
            }
            if (null == modelSource)
            {
                throw new ArgumentNullException("modelSource");
            }

            this.vmRoot = vmMgr;
            this.model = modelSource;
            this.CancelCommand = new DelegateCommand(() =>
            {
                if (!this.IsWorking)
                {
                    this.vmRoot.DefaultBackCommand.Execute();
                }
            },
            () => !this.IsWorking);
            this.SaveCommand = new DelegateCommand(() =>
            {
                if (!this.IsWorking)
                {
                    DoServerSave();
                }
            },
            () => !this.IsWorking);
        }

        /// <summary>
        /// Cancels out of adding a new server.
        /// </summary>
        public DelegateCommand CancelCommand
        {
            get;
            private set;
        }

        /// <summary>
        /// Tests the new server and saves it if it is valid.
        /// </summary>
        public DelegateCommand SaveCommand
        {
            get;
            private set;
        }

        /// <summary>
        /// A more specific error message to show to the user.
        /// </summary>
        public string ErrorMessage
        {
            get
            {
                lock (this.stateLock)
                {
                    return this.errorMessage;
                }
            }
            private set
            {
                bool changed = false;
                lock (this.stateLock)
                {
                    if (!string.Equals(value, this.errorMessage))
                    {
                        changed = true;
                        this.errorMessage = value;
                    }
                }

                if (changed)
                {
                    OnPropertyChanged("ErrorMessage");
                }
            }
        }

        /// <summary>
        /// The server name.
        /// </summary>
        public string ServerName
        {
            get
            {
                return this.serverName;
            }
            set
            {
                this.vmRoot.AssertUiThread();

                if (!string.Equals(this.serverName, value, StringComparison.OrdinalIgnoreCase))
                {
                    this.serverName = value;
                    OnPropertyChanged("ServerName");
                }
            }
        }

        /// <summary>
        /// Returns a status message to display on the UI.
        /// </summary>
        public string StatusMessage
        {
            get
            {
                lock (this.stateLock)
                {
                    return this.statusMessage;
                }
            }
            private set
            {
                lock (this.stateLock)
                {
                    if (this.statusMessage != value)
                    {
                        this.statusMessage = value;
                        this.vmRoot.UiDispatch(() => OnPropertyChanged("StatusMessage"));
                    }
                }
            }
        }

        /// <summary>
        /// Used to control the enabled state of the server text box - we disable it while we
        /// are testing a server name.
        /// </summary>
        public bool CanEditServer
        {
            get
            {
                lock (this.stateLock)
                {
                    return !this.working;
                }
            }
        }

        /// <summary>
        /// Whether we are working, testing a server name.
        /// </summary>
        private bool IsWorking
        {
            get
            {
                lock (this.stateLock)
                {
                    return this.working;
                }
            }
            set
            {
                lock (this.stateLock)
                {
                    if (this.working != value)
                    {
                        this.working = value;
                        this.vmRoot.UiDispatch(() =>
                            {
                                OnPropertyChanged("CanEditServer");
                            });
                    }
                }
            }
        }

        /// <summary>
        /// Command implementation for the save button. First the server is tested to see if it
        /// is valid. If it is we pass the server back to the settings page, otherwise we
        /// display an error.
        /// </summary>
        private void DoServerSave()
        {
            // Setup the UI to display the working status.
            this.IsWorking = true;
            this.StatusMessage = Properties.Resources.AddServerWorkingStatus;
            this.SaveCommand.RaiseCanExecuteChanged();
            this.CancelCommand.RaiseCanExecuteChanged();

            //
            // Run the actual test call in a worker thread as it can take a while, especially if
            // the name isn't valid.
            //
            ThreadPool.QueueUserWorkItem((s) =>
            {
                bool success = false;
                try
                {
                    // Do the test of the new server name.
                    if (!string.IsNullOrEmpty(this.serverName))
                    {
                        string error;
                        success = this.model.TestServerName(this.serverName, out error);
                        ErrorMessage = error;
                    }
                }
                finally
                {
                    // Update the UI to reflect the result.
                    this.IsWorking = false;
                    this.vmRoot.UiDispatch(() =>
                    {
                        if (success)
                        {
                            this.StatusMessage = null;
                            this.vmRoot.SettingsViewModel.AddNewServer(this.serverName);

                            // Auto-close the add server page on the successful entry of a valid
                            // server.
                            this.vmRoot.DefaultBackCommand.Execute();
                        }
                        else
                        {
                            this.StatusMessage = Properties.Resources.AddServerVerifyFailed;
                        }

                        this.SaveCommand.RaiseCanExecuteChanged();
                        this.CancelCommand.RaiseCanExecuteChanged();
                    });
                }
            });
        }

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Fired whenever a public property changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Fires the property change event.
        /// </summary>
        /// <param name="propertyName">The name of the property that changed.</param>
        private void OnPropertyChanged(string propertyName)
        {
            NotifyHelper.VerifyProperty(propertyName, this);
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged.Raise(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion // INotifyPropertyChanged Members

        #region IPage Members

        /// <summary>
        /// Used to notify the page when is made the active page, or when it has become inactive.
        /// </summary>
        /// <param name="isActive">True if this page is now active, false if it has become
        /// inactive.</param>
        public void NotifyActiveChanged(bool isActive)
        {
            if (isActive)
            {
                this.StatusMessage = null;
                this.ServerName = null;
            }
        }

        /// <summary>
        /// Notifies the active page that the physical back button has been pressed on the
        /// client device.
        /// </summary>
        public void NotifyBackButtonPressed()
        {
            if (!this.IsWorking)
            {
                this.vmRoot.DefaultBackCommand.Execute();
            }
        }

        /// <summary>
        /// Used to access the back command that will navigate you from the current page to the
        /// previous page in the navigation history.
        /// </summary>
        public ViewModelManager VmManager
        {
            get { return this.vmRoot; }
        }

        #endregion
    }
}
